package org.hairball;

import org.hairball.analysis.AnalysisGrapher;
import org.hairball.analysis.CompositeClassAnalyser;
import org.hairball.analysis.RelationshipAnalyser;
import org.hairball.graph.HairballGraph;
import org.hairball.graph.NodeFilteringGraph;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class HairballGraphFactory {
    private final ClassJarReader reader;

    public HairballGraphFactory() {
        this.reader = new ClassJarReader();
    }

    public HairballGraphFactory(ClassJarReader reader) {
        this.reader = reader;
    }

    public HairballGraph create(ClassInclusionFilter filter, Set<AnalysisType> analysisTypes, String jarFile) throws IOException {
        List<ClassNode> classNodes = readClassNodesFromJar(jarFile);

        AnalysisGrapher grapher = new AnalysisGrapher(
              new CompositeClassAnalyser(analysers(analysisTypes))
        );

        HairballGraph graph = grapher.graph(classNodes.toArray(new ClassNode[0]));
        return new NodeFilteringGraph(graph, filter);
    }

    private RelationshipAnalyser[] analysers(Set<AnalysisType> analysisTypes) {
        RelationshipAnalyser analysers[] = new RelationshipAnalyser[analysisTypes.size()];
        int index = 0;
        for (AnalysisType analysisType : analysisTypes) {
            analysers[index] = analysisType.getClassAnalyser();
            index++;
        }
        return analysers;
    }

    private List<ClassNode> readClassNodesFromJar(String jarFileToLoad) throws IOException {
        List<byte[]> list = reader.read(jarFileToLoad);
        List<ClassNode> classNodes = new ArrayList<ClassNode>();
        for (byte[] bytes : list) {
            ClassReader classReader = new ClassReader(bytes);
            ClassNode classNode = new ClassNode();
            classReader.accept(classNode, 0);
            classNodes.add(classNode);
        }
        return classNodes;
    }
}
